package com.dong.auth.filter;

import com.dong.auth.module.user.service.SysUserService;
import com.dong.auth.security.authentication.Authentication;
import com.dong.auth.security.authentication.AuthenticationManager;
import com.dong.auth.security.authentication.holder.DongAuthenticationHolder;
import com.dong.auth.security.exception.AuthenticationException;
import com.dong.auth.security.exception.AuthenticationFailedException;
import com.dong.auth.security.matcher.AntPathRequestMatcher;
import com.dong.auth.security.matcher.RequestMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 处理用户登录请求的filter
 *
 * @author zhaodongchao
 * @version V1.0
 * @date 2021/10/13 20:20
 */
@WebFilter(urlPatterns = "/login", filterName = "loginFilter")
public class LoginFilter implements Filter {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    public static final String LOGIN_PROCESS_URL = "/login";
    private static final String FILTER_authentication_APPLIED = "__dong_security_authentication_filter_applied";
    private final RequestMatcher requiresAuthenticationRequestMatcher = new AntPathRequestMatcher("/login");
    private final String usernameParameter = "username";
    private final String passwordParameter = "password";

    @Resource
    private AuthenticationManager authenticationManager;


    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        //1校验请求的url和方式
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationFailedException("Authentication method not supported: " + request.getMethod());
        }
        //2 丛请求中获取登录的用户名和密码等信息
        String username = request.getParameter(usernameParameter);
        username = null == username ? "" : username.trim();
        String password = request.getParameter(passwordParameter);

        return authenticationManager.authenticate(Authentication.ofLogin(username, password));
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        // 1 判断请求是否是登录请求
        String requestURI = httpServletRequest.getRequestURI();
        if (!LOGIN_PROCESS_URL.equals(requestURI)) {
            chain.doFilter(request, response);
            return;
        }

        if (httpServletRequest.getMethod().equals("GET")) {
            if (httpServletRequest.getAttribute(FILTER_authentication_APPLIED) != null) {
                chain.doFilter(request, response);
                return;
            }
            httpServletRequest.setAttribute(FILTER_authentication_APPLIED, Boolean.TRUE);
            //判断当前是否登录
            Authentication authentication = DongAuthenticationHolder.get();
            if (null != authentication && !authentication.isAuthenticated()) {
                httpServletResponse.sendRedirect("/");
                return;
            }
            //确实没有登录，就跳到登录页面
            httpServletRequest.getRequestDispatcher(LOGIN_PROCESS_URL).forward(request, response);
            return;
        }


        //1 判断请求是否是特殊的认证身份的URL
        if (!requiresAuthenticationRequestMatcher.matches(httpServletRequest)) {
            chain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }
        //2 开始进行认证操作 ！！！
        Authentication authResult = null;
        try {
            authResult = attemptAuthentication(httpServletRequest, httpServletResponse);
            if (authResult == null) {
                //如果认证失败就返回
                return;
            }
        } catch (AuthenticationException e) {
            e.printStackTrace();
            handleAuthenticationException(httpServletResponse);
            return;
        }
        //3 处理session

        //4 登录成功之后进行相关操作
        successfulAuthentication(httpServletRequest, httpServletResponse, authResult);
    }

    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException {
        //1 将登录成功后的认证信息存入 AuthenticationHolder 容器中
        DongAuthenticationHolder.set(authResult);
        //2 TODO 执行remember me操作. 设置session过期时间

        //3 跳转到登录成功页面
        //request.getRequestDispatcher("/").forward(request, response);
        response.sendRedirect("/");
    }
    private void handleAuthenticationException(HttpServletResponse response) throws IOException {
        Authentication authentication = DongAuthenticationHolder.get();
        if (authentication == null || !authentication.isAuthenticated()){
            //此时没有登录，需要进行登录操作
            //TODO 缓存当前没有权限访问的请求，等登录成功之后继续访问当前请求
            String loginForm = response.encodeRedirectURL(LoginFilter.LOGIN_PROCESS_URL);
            response.sendRedirect(loginForm);
        }
    }

    public AuthenticationManager getAuthenticationManager() {
        return authenticationManager;
    }

    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }
}
